// Yang Shuai  Copyright (c) 2022 https://yangbuyi.top.
// Copyright (c) 2022. Yangbuyi, personal projects are not allowed to be commercialized without permission.
// Please keep the information of the original author of the code. Thank you

package top.yangbuyi.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollectionUtil;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import top.yangbuyi.domain.AjaxResult;
import top.yangbuyi.domain.SysUserFaceInfo;
import top.yangbuyi.dto.FaceSearchResDto;
import top.yangbuyi.dto.FaceUserInfo;
import top.yangbuyi.dto.ProcessInfo;
import top.yangbuyi.enums.ErrorCodeEnum;
import top.yangbuyi.service.FaceEngineService;
import top.yangbuyi.service.SysUserFaceInfoService;
import top.yangbuyi.service.impl.SysUserFaceInfoServiceImpl;
import org.springframework.web.bind.annotation.*;
import top.yangbuyi.utils.ImageUtils;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.security.auth.Subject;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

/**
 * (sys_user_face_info)表控制层
 *
 * @author yangbuyi.top
 */
@RestController
@Slf4j
@RequestMapping("face")
@RequiredArgsConstructor
public class SysUserFaceInfoController {
    public final static Logger logger = LoggerFactory.getLogger(SysUserFaceInfoController.class);

    private final FaceEngineService faceEngineService;

    private final SysUserFaceInfoService userFaceInfoService;


    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    @GetMapping("selectOne")
    public SysUserFaceInfo selectOne (Integer id) {
        return this.userFaceInfoService.lambdaQuery().eq(SysUserFaceInfo::getId, id).one();
    }


    /**
     * 人脸添加
     *
     * @param file    人脸附件
     * @param groupId 分组id
     * @param name    用户登录名称
     */
    @RequestMapping(value = "/faceAdd", method = RequestMethod.POST)
    public AjaxResult faceAdd (@RequestBody Map<String, Object> map) {
        String file = String.valueOf(map.get("file"));
        String groupId = String.valueOf(map.get("groupId"));
        String name = String.valueOf(map.get("name"));
        try {
            if (file == null) {
                return AjaxResult.error("file is null");
            }
            if (groupId == null) {
                return AjaxResult.error("file is null");
            }
            if (name == null) {
                return AjaxResult.error("file is null");
            }
            // 转换实体
            byte[] decode = Base64.decode(base64Process(file));
            ImageInfo imageInfo = ImageFactory.getRGBData(decode);

            //人脸特征获取
            byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
            if (bytes == null) {
                return AjaxResult.error(ErrorCodeEnum.NO_FACE_DETECTED.getDescription());
            }
            List<ProcessInfo> processInfoList = faceEngineService.process(imageInfo);
            // 开始将人脸识别Base64转换文件流
            final MultipartFile multipartFile = ImageUtils.base64ToMultipartFile(file);
            final SysUserFaceInfo one = this.userFaceInfoService.lambdaQuery().eq(SysUserFaceInfo::getName, name).one();
            // 如果存在则更新人脸和特征
            if (null != one) {
                this.userFaceInfoService.lambdaUpdate().set(SysUserFaceInfo::getFaceFeature, bytes)
                        .set(SysUserFaceInfo::getFpath, "存储头像地址")
                        .eq(SysUserFaceInfo::getFaceId, name).update();
            } else {
                // 组装人脸实体
                SysUserFaceInfo userFaceInfo = new SysUserFaceInfo();
                userFaceInfo.setName(name);
                userFaceInfo.setAge(processInfoList.get(0).getAge());
                userFaceInfo.setGender(processInfoList.get(0).getGender().shortValue());
                userFaceInfo.setGroupId(Integer.valueOf(groupId));
                userFaceInfo.setFaceFeature(bytes);
                userFaceInfo.setFpath("存储头像地址");
                // 存储用户ID -> 我这里先使用name代替 -> 假如是唯一性
                userFaceInfo.setFaceId(name);
                //人脸特征插入到数据库
                userFaceInfoService.insertSysUserFaceInfo(userFaceInfo);
            }
            logger.info("faceAdd:" + name);
            return AjaxResult.success("人脸绑定成功!");
        } catch (Exception e) {
            logger.error("", e);
        }
        return AjaxResult.error(ErrorCodeEnum.UNKNOWN.getDescription());
    }

    /**
     * 人脸识别
     *
     * @param file    人脸附件
     * @param groupId 分组ID 方便快速识别
     */
    @RequestMapping(value = "/faceSearch", method = RequestMethod.POST)
    public AjaxResult faceSearch (@RequestBody Map<String, Object> map) throws Exception {
        String file = String.valueOf(map.get("file"));
        String groupId = String.valueOf(map.get("groupId"));

        if (groupId == null) {
            return AjaxResult.error("groupId is null");
        }
        byte[] decode = Base64.decode(base64Process(file));
        BufferedImage bufImage = ImageIO.read(new ByteArrayInputStream(decode));
        ImageInfo imageInfo = ImageFactory.bufferedImage2ImageInfo(bufImage);


        //人脸特征获取
        byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
        // 校验是否显示出人脸
        if (bytes == null) {
            return AjaxResult.error(ErrorCodeEnum.NO_FACE_DETECTED.getDescription());
        }

        //人脸比对，获取比对结果
        List<FaceUserInfo> userFaceInfoList = faceEngineService.compareFaceFeature(bytes, Integer.valueOf(groupId));

        if (CollectionUtil.isNotEmpty(userFaceInfoList)) {
            FaceUserInfo faceUserInfo = userFaceInfoList.get(0);
            FaceSearchResDto faceSearchResDto = new FaceSearchResDto();
            BeanUtil.copyProperties(faceUserInfo, faceSearchResDto);
            List<ProcessInfo> processInfoList = faceEngineService.process(imageInfo);
            if (CollectionUtil.isNotEmpty(processInfoList)) {
                //人脸检测
                List<FaceInfo> faceInfoList = faceEngineService.detectFaces(imageInfo);
                int left = faceInfoList.get(0).getRect().getLeft();
                int top = faceInfoList.get(0).getRect().getTop();
                int width = faceInfoList.get(0).getRect().getRight() - left;
                int height = faceInfoList.get(0).getRect().getBottom() - top;

                Graphics2D graphics2D = bufImage.createGraphics();
                // 红色
                graphics2D.setColor(Color.RED);
                BasicStroke stroke = new BasicStroke(5f);
                graphics2D.setStroke(stroke);
                graphics2D.drawRect(left, top, width, height);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                ImageIO.write(bufImage, "jpg", outputStream);
                byte[] bytes1 = outputStream.toByteArray();
                faceSearchResDto.setImage("data:image/jpeg;base64," + Base64Utils.encodeToString(bytes1));
                faceSearchResDto.setAge(processInfoList.get(0).getAge());
                faceSearchResDto.setGender(processInfoList.get(0).getGender().equals(1) ? "女" : "男");
            }
            return AjaxResult.success(faceSearchResDto);
        }
        return AjaxResult.error(ErrorCodeEnum.FACE_DOES_NOT_MATCH.getDescription());
    }


    /**
     * 图片处理
     *
     * @param base64Str
     * @return base64Process
     */
    private String base64Process (String base64Str) {
        if (!StringUtils.isEmpty(base64Str)) {
            String photoBase64 = base64Str.substring(0, 30).toLowerCase();
            int indexOf = photoBase64.indexOf("base64,");
            if (indexOf > 0) {
                base64Str = base64Str.substring(indexOf + 7);
            }

            return base64Str;
        } else {
            return "";
        }
    }
}
